# -*- coding:binary -*-

require 'spec_helper'

RSpec.describe Msf::Exploit::Remote::HTTP::Pihole do
  subject do
    mod = ::Msf::Module.new
    mod.extend described_class
    mod
  end

  let(:valid_password) do
    'password'
  end

  let(:valid_cookie) do
    'PHPSESSID=ma4vsmicoesr8ajajraehad6mb'
  end

  let(:valid_code) do
    200
  end

  let(:tab) do
    'api'
  end

  let(:token_no_slash) do
    't51q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk='
  end

  let(:token_slash) do
    't5/q3YuxWT873Nn+6lCyMG4Lg840gRCgu03akuXcvTk='
  end

  describe '#pihole_versions' do
    it 'returns nil if page can not be reached' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response::E404.new
        res
      end

      expect(subject.get_versions).to be_nil
    end

    it 'returns [nil,nil,nil] when page can be reached but isn\'t a Pihole' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = 'Hello World'
        res
      end

      expect(subject.get_versions).to eq([nil, nil, nil])
    end

    it 'returns 5.7, 5.9, 5.12.1, to version 5.7, 5.9, 5.12.1' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = "<li>\n"
        res.body << "<strong>Pi-hole</strong>\n"
        res.body << "<a href=\"https://github.com/pi-hole/pi-hole/releases/v5.7\" rel=\"noopener\" target=\"_blank\">v5.7</a>\n"
        res.body << "                    </li>\n"
        res.body << "<li>\n"
        res.body << "<strong>FTL</strong>\n"
        res.body << "<a href=\"https://github.com/pi-hole/FTL/releases/v5.12.1\" rel=\"noopener\" target=\"_blank\">v5.12.1</a>\n"
        res.body << "                    </li>\n"
        res.body << "<li>\n"
        res.body << "<strong>Web Interface</strong>\n"
        res.body << "<a href=\"https://github.com/pi-hole/AdminLTE/releases/v5.9\" rel=\"noopener\" target=\"_blank\">v5.9</a>\n"
        res.body << "                    </li>\n"
        res.body << "</ul>\n"
        res
      end

      expect(subject.get_versions).to eq(['5.7', '5.9', '5.12.1'])
    end

    it 'returns 5.2.2, 5.2.2, 5.3.3 to version 5.2.2, 5.2.2, 5.3.3' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = "<li>\n"
        res.body << "<strong>Pi-hole</strong>\n"
        res.body << "<a href=\"https://github.com/pi-hole/pi-hole/releases/v5.2.2\" rel=\"noopener\" target=\"_blank\">v5.2.2</a>\n"
        res.body << "&middot; <a class=\"lookatme\" href=\"https://github.com/pi-hole/pi-hole/releases/latest\" rel=\"noopener\" target=\"_blank\">Update available!</a>                    </li>\n"
        res.body << "<li>\n"
        res.body << "<strong>Web Interface</strong>\n"
        res.body << "<a href=\"https://github.com/pi-hole/AdminLTE/releases/v5.2.2\" rel=\"noopener\" target=\"_blank\">v5.2.2</a>\n"
        res.body << "&middot; <a class=\"lookatme\" href=\"https://github.com/pi-hole/AdminLTE/releases/latest\" rel=\"noopener\" target=\"_blank\">Update available!</a>                    </li>\n"
        res.body << "<li>\n"
        res.body << "<strong>FTL</strong>\n"
        res.body << "<a href=\"https://github.com/pi-hole/FTL/releases/v5.3.3\" rel=\"noopener\" target=\"_blank\">v5.3.3</a>\n"
        res.body << "&middot; <a class=\"lookatme\" href=\"https://github.com/pi-hole/FTL/releases/latest\" rel=\"noopener\" target=\"_blank\">Update available!</a>                    </li>\n"
        res
      end

      expect(subject.get_versions).to eq(['5.2.2', '5.2.2', '5.3.3'])
    end

    it 'returns 4.4, 4.3.3, 4.3.1 to version 4.4, 4.3.3, 4.3.1' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = '<b>Pi-hole Version </b> v4.4 <a class="alert-link lookatme" href="https://github.com/pi-hole/pi-hole/releases" target="_blank">(Update available!)</a>            <b>Web Interface Version </b>v4.3.3 <a class="alert-link lookatme" href="https://github.com/pi-hole/AdminLTE/releases" target="_blank">(Update available!)</a>            <b>FTL Version </b> v4.3.1 <a class="alert-link lookatme" href="https://github.com/pi-hole/FTL/releases" target="_blank">(Update available!)</a>        </div>'
        res
      end

      expect(subject.get_versions).to eq(['4.4', '4.3.3', '4.3.1'])
    end

    it 'returns 4.3.2, 4.3, 4.3.1 to version 4.3.2, 4.3, 4.3.1' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = '<b>Pi-hole Version </b> v4.3.2            <b>Web Interface Version </b>v4.3            <b>FTL Version </b> v4.3.1        </div>'
        res
      end

      expect(subject.get_versions).to eq(['4.3.2', '4.3', '4.3.1'])
    end

    it 'vDev (HEAD) as of 4.3' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = "<ul class=\"list-unstyled\">\n"
        res.body << "<li><strong>Pi-hole</strong> vDev (HEAD, v4.3-0-g1d43c0a-dirty)</li>\n"
        res.body << "<li><strong>Web Interface</strong> v5.5</li>\n"
        res.body << "<li><strong>FTL</strong> v4.3.1</li>\n"
        res.body << "</ul>\n"
        res
      end

      expect(subject.get_versions).to eq(['4.3', '5.5', '4.3.1'])
    end
  end

  describe '#pihole_logins' do
    it 'returns nil if page can not be reached' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response::E404.new
        res
      end

      expect(subject.login(valid_password)).to be_nil
    end

    it 'returns nil on bad login' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = 'Sign in to start your session'
        res
      end

      expect(subject.login(valid_password)).to be_nil
    end

    it 'returns cookie on valid login' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.headers['Set-Cookie'] = "#{valid_cookie};"
        res.body = 'page'
        res
      end

      expect(subject.login(valid_password)).to include(valid_cookie)
    end
  end

  describe '#pihole_get_token' do
    it 'returns nil if page can not be reached' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response::E404.new
        res
      end

      expect(subject.get_token(:tab)).to be_nil
    end

    it 'returns nil when no token' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = 'Sign in to start your session'
        res
      end

      expect(subject.get_token(:tab)).to be_nil
    end

    it 'returns token without slashes' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = '<input type="hidden" name="token" value="token_no_slash">'
        res
      end

      expect(subject.get_token(:tab)).to include(:token_no_slash.to_s)
    end

    it 'returns token with slashes' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = '<input type="hidden" name="token" value="token_slash">'
        res
      end

      expect(subject.get_token(:tab)).to include(:token_slash.to_s)
    end

    it 'returns token with slashes 3.3 format' do
      allow(subject).to receive(:send_request_cgi) do
        res = Rex::Proto::Http::Response.new
        res.code = valid_code
        res.body = '<div id="token" hidden>token_no_slash</div>'
        res
      end

      expect(subject.get_token(:tab)).to include(:token_no_slash.to_s)
    end
  end
end
